//  Copyright (C) 2002 UltraVNC Team Members. All Rights Reserved.
//  Copyright (C) 2000-2002 Const Kaplinsky. All Rights Reserved.
//  Copyright (C) 2002 RealVNC Ltd. All Rights Reserved.
//  Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
//
//  This file is part of the VNC system.
//
//  The VNC system is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
//  USA.
//
// If the source code for the VNC system is not available from the place 
// whence you received this file, check http://www.uk.research.att.com/vnc or contact
// the authors on vnc@uk.research.att.com for information on obtaining it.

// vncDesktop implementation

// System headers
#include <assert.h>
#include "stdhdrs.h"

// Custom headers
//#include <WinAble.h>
#include <omnithread.h>
#include "winvnc.h"
#include "vnchooks/VNCHooks.h"
#include "vncserver.h"
#include "vnckeymap.h"
#include "rfbRegion.h"
#include "rfbRect.h"
#include "vncdesktop.h"
#include "vncservice.h"
// Modif rdv@2002 - v1.1.x - videodriver
#include "vncOSVersion.h"
#include "DeskdupEngine.h"

#include "mmsystem.h" // sf@2002
#include "TextChat.h" // sf@2002
#include "vncdesktopthread.h"
#include "common/win32_helpers.h"
#include <algorithm>
#include <commctrl.h>

extern bool PreConnect;
int getinfo(char mytext[1024]);



// Constants
const UINT RFB_SCREEN_UPDATE = RegisterWindowMessage("WinVNC.Update.DrawRect");
const UINT RFB_COPYRECT_UPDATE = RegisterWindowMessage("WinVNC.Update.CopyRect");
const UINT RFB_MOUSE_UPDATE = RegisterWindowMessage("WinVNC.Update.Mouse");
const char szDesktopSink[] = "WinVNC desktop sink";

bool g_Desktop_running;
extern bool g_DesktopThread_running;
extern char g_hookstring[16];

//
// // Modif sf@2002 - v1.1.0 - Optimization
//
// Here we try to speed up the Poll FullScreen function.
// Instead of adding the entire Rect to cached region,
// we divide it in 32/32 pixels blocks and only scan the top-left
// pixel of each block.
// If a pixel has changed, we find the smallest window containing this pixel
// and add the Window rect to the cached region
//
// This function is supposed to be a good compromise between
// speed and efficiency.
// The accuracy has been greatly improved and it now detects very
// small changes on screen that are not detected by the Hooks.
// This new accuracy is obtained by shifting the detection grid 
// in diagonale (the function works with several grids that are tested 
// alternatively). There's probably still room for improvements...
//
// NB: Changes generated by applications using hardware acceleration 
// won't be detected (In order to see a video played on the server using
// Media Player, the vncviewer user must desactivate "Hardware accelation"
// in the Media Player options). 
// 
// 
//
extern bool G_USE_PIXEL;
PixelCaptureEngine::~PixelCaptureEngine()
{
}

PixelCaptureEngine::PixelCaptureEngine()
{
	if (VNCOS.OS_VISTA || VNCOS.OS_WIN7 || VNCOS.OS_WIN8 || VNCOS.OS_WIN10) 
		m_bIsVista = true;
	else
		m_bIsVista = false;

	if (G_USE_PIXEL)
		m_bIsVista = false;
}
void
PixelCaptureEngine::PixelCaptureEngineInit(HDC rootdc, HDC memdc, HBITMAP membitmap, bool bCaptureAlpha, void *dibbits, int bpp, int bpr, int offsetx, int offsety)
{
	m_hmemdc = memdc;
	m_membitmap = membitmap;
	m_oldbitmap = 0;
	m_DIBbits = dibbits;
	m_bCaptureAlpha = bCaptureAlpha;
	m_hrootdc_PixelEngine = rootdc;
	m_bytesPerPixel = bpp;
	m_bytesPerRow = bpr;
	m_ScreenOffsetx = offsetx;
	m_ScreenOffsety = offsety;
}

//SImulate high load biblt
void Highcpu(long counter)
{
	float number = 1.5;

	while (counter > 0)
	{
		number *= number;
		counter--;
	}

}

bool
PixelCaptureEngine::CaptureRect(const rfb::Rect& rect)
{
	if (m_bIsVista)
	{
		m_rect = rect;
		if ((m_oldbitmap = (HBITMAP)SelectObject(m_hmemdc, m_membitmap)) == NULL)
			return false;
		//Highcpu(100000000);
//			Beep(1000, 100);
			// Capture screen into bitmap
		BOOL blitok = BitBlt(m_hmemdc, 0, 0, rect.width(), rect.height(), m_hrootdc_PixelEngine, rect.tl.x + m_ScreenOffsetx, rect.tl.y + m_ScreenOffsety,
			m_bCaptureAlpha ? (CAPTUREBLT | SRCCOPY) : SRCCOPY);

#ifdef _DEBUG
			OutputDevMessage("BitBlt  %i %i %i %i",rect.tl.x,
			rect.tl.y,
			rect.br.x,
			rect.br.y);
#endif
		return blitok ? true : false;
	}
	return true;
}

COLORREF
PixelCaptureEngine::CapturePixel(int x, int y)
{
	if (m_bIsVista)
	{
		COLORREF cr = 0;
		int tx = x - m_rect.tl.x;
		int ty = y - m_rect.tl.y;

		unsigned int index = (m_bytesPerRow * y) + (m_bytesPerPixel * x);
		memcpy(&cr, ((char*)m_DIBbits) + index, m_bytesPerPixel);

		return cr;
	}
	else
	{

		COLORREF cr = 0;
		cr = GetPixel(m_hrootdc_PixelEngine, x, y);
		return cr;
	}
}

void
PixelCaptureEngine::ReleaseCapture()
{
	// Select the old bitmap back into the memory DC
	if (m_bIsVista)
	{
		SelectObject(m_hmemdc, m_oldbitmap);
		m_oldbitmap = 0;
	}
}


bool vncDesktop::FastDetectChanges(rfb::Region2D &rgn, rfb::Rect &rect, int nZone, bool fTurbo)
{
	if (m_DIBbits == NULL) return false;
	bool returnvalue = false;
	bool fInitGrid = false;
	bool fIncCycle = false;
	// For more accuracy, we could use 24 or even 16
	const int PIXEL_BLOCK_SIZE = 32; // Pixels Grid definition
	const int GRID_OFFSET = 4;  // Pixels Grid shifting during cycle (in pixels)
								// The number of grid per zone is PIXEL_BLOCK_SIZE/GRID_OFFSET (must be int !)
	int x, y;
	int xo, yo;
	// WindowsList lWList;
	WindowsList::iterator iWindow;

	// In turbo mode, we clear the list of windows at each iteration -> Lot of updates because 
	//  the same windows can be detected several times during a complete cycle
	// (To avoid this pb, we must update all the grids at the same coordinate when a pixel of 
	// one grid has changed -> later)
	// Otherwise we only clear it each time the Grid cycle loops -> Less updates, less framerate,
	// less CPU, less bandwidth
	if (fTurbo || (m_nGridCycle == 0))
		m_lWList.clear();

	// Create all the Grids (for all the 5 zones (4 quarter screens + 1 full screen)
	// Actually, the quarter screens are not utilized in v1.1.0 but it does not
	// generate any overhead neither additionnal memory consumption (see below)
	if (m_lGridsList.empty())
	{
		for (int i = 0; i < (5 * PIXEL_BLOCK_SIZE / GRID_OFFSET); i++)
		{
			RGBPixelList *pList = new RGBPixelList;
			pList->reserve((rect.height() / PIXEL_BLOCK_SIZE) * (rect.width() / PIXEL_BLOCK_SIZE));
			if (pList != NULL)
			{
				m_lGridsList.push_back(pList);
				vnclog.Print(LL_INTINFO, VNCLOG("### PixelsGrid %d created !\n"), i);
			}
		}

		hDeskWnd = GetDesktopWindow();
		HWND hWnd = FindWindow(_T("Progman"), _T("Program Manager"));
		if (NULL != hWnd) hWnd = FindWindowEx(hWnd, NULL, "SHELLDLL_DefView", "");
		if (NULL != hWnd) hWnd = FindWindowEx(hWnd, NULL, "SysListView32", "FolderView");
		hFolderView = hWnd;
		if (NULL != hWnd)
		{
			nr_rects = (int)SendMessage(hWnd, LVM_GETITEMCOUNT, 0, 0);
			if (nr_rects > 200) nr_rects = 200;
			for (int j = 0; j < nr_rects; j++)
			{
				DWORD pid;
				GetWindowThreadProcessId(hWnd, &pid);
				HANDLE hProcess = NULL;
				hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
				RECT* ptritemrect;
				RECT itemrect;
				ptritemrect = (RECT*)VirtualAllocEx(hProcess, NULL, sizeof(RECT), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
				if (ptritemrect == NULL) {

				}
				else {
					SendMessage(hWnd, LVM_GETITEMRECT, j, (LPARAM)ptritemrect);
#ifdef _X64
					SIZE_T copied = 0;
#else
					DWORD copied = 0;
#endif
					ReadProcessMemory(hProcess, (void*)ptritemrect, (LPVOID)&itemrect, sizeof(itemrect), &copied);
					rfb::Rect wrect = rfb::Rect(itemrect).intersect(m_Cliprect);
					iconregion.assign_union(wrect);
					VirtualFreeEx(hProcess, ptritemrect, 0, MEM_RELEASE);
				}
				if (hProcess) CloseHandle(hProcess);
			}
		}

	}

	PixelEngine.PixelCaptureEngineInit(m_hrootdc_Desktop, m_hmemdc, m_membitmap, VNCOS.CaptureAlphaBlending() && !m_Black_window_active,
		m_DIBbits, m_scrinfo.format.bitsPerPixel / 8, m_bytesPerRow, m_ScreenOffsetx, m_ScreenOffsety);
	// We test one zone at a time 
	// vnclog.Print(LL_INTINFO, VNCLOG("### Polling Grid %d - SubGrid %d\n"), nZone, m_nGridCycle); 
	GridsList::iterator iGrid;
	int nGridPos = (nZone * PIXEL_BLOCK_SIZE / GRID_OFFSET) + m_nGridCycle;
	int nIndex = nGridPos;

	iGrid = m_lGridsList.begin();
	std::advance(iGrid, nGridPos);

	RGBPixelList *pThePixelGrid = *iGrid;
	RGBPixelList::iterator iPixelColor = pThePixelGrid->begin();

	if (nZone == 0 || nZone == 4)
	{
		// vnclog.Print(LL_INTINFO, VNCLOG("### IncCycle Please !\n")); 
		fIncCycle = true;
	}

	if (pThePixelGrid->empty())
	{
		// vnclog.Print(LL_INTINFO, VNCLOG("### PixelsGrid Init\n"));
		fInitGrid = true;
	}

	int nOffset = GRID_OFFSET * m_nGridCycle;


	PixelEngine.CaptureRect(rect);
	if (PixelEngine.m_bIsVista) returnvalue = true;
	// Try to detect if screen is almost idle
	// no need to poll static screens very fast, it only use cpu
	change_found = 0;

	// We walk our way through the Grids
	for (y = rect.tl.y; y < (rect.br.y - nOffset - 1); y += PIXEL_BLOCK_SIZE)
	{
		yo = y + nOffset;

		for (x = rect.tl.x; x < (rect.br.x - nOffset); x += PIXEL_BLOCK_SIZE)
		{
			xo = x + nOffset;
			bool AlreadyInRegion = rgn.IsPtInRegion(xo, yo);
			// Read the pixel's color on the screen
			COLORREF PixelColor = 0;
			//if (!AlreadyInRegion || fInitGrid ) 
			PixelColor = PixelEngine.CapturePixel(xo, yo);

			// If init list
			if (fInitGrid)
			{
				int off = (int)(iPixelColor - pThePixelGrid->begin());
				pThePixelGrid->push_back(PixelColor);
				iPixelColor = pThePixelGrid->begin() + off;
				rgn.assign_union(rect);
				// vnclog.Print(LL_INTINFO, VNCLOG("### PixelsGrid Init : Pixel xo=%d - yo=%d - C=%ld\n"), xo, yo, (long)PixelColor); 
				continue;
			}

			//			vnclog.Print(LL_INTINFO, VNCLOG("### GetPixel %i\n"),OSversion());
						// If the pixel has changed
			if (*iPixelColor != PixelColor)
			{
				change_found = 1;
				// Save the new Pixel in the list
				*iPixelColor = PixelColor;
				if (!AlreadyInRegion)
				{
					// Then find the corresponding Window
					POINT point;
					RECT rect;

					point.x = xo + m_ScreenOffsetx;
					point.y = yo + m_ScreenOffsety;

					// Find the smallest, non-hidden, non-disabled Window containing this pixel
					// REM: We don't use ChildWindowFromPoint because we don't want of hidden windows
					HWND hwnd = WindowFromPoint(point);

					/// This is the fulldesktop, cause a full scan performance !
					if (hFolderView == hwnd && hFolderView)
					{
						if (iconregion.IsPtInRegion(xo, yo))rgn.assign_union(iconregion);
						else
						{
							rect.left = xo - PIXEL_BLOCK_SIZE - m_ScreenOffsetx;
							rect.right = xo + PIXEL_BLOCK_SIZE - m_ScreenOffsetx;
							rect.top = yo - PIXEL_BLOCK_SIZE - m_ScreenOffsetx;
							rect.bottom = yo + PIXEL_BLOCK_SIZE - m_ScreenOffsetx;
							rfb::Rect wrect = rfb::Rect(rect).intersect(m_Cliprect);
							if (!wrect.is_empty())
							{
								rgn.assign_union(wrect);
							}
						}
					}
					// Look if we've already detected this window
					if (hwnd != hDeskWnd && hFolderView != hwnd)
					{

						// Look if we've already detected this window
						if (std::find(m_lWList.begin(), m_lWList.end(), hwnd) == m_lWList.end())
						{
							// Add the corresponding rect to the cache region 
							if (GetWindowRect(hwnd, &rect))
							{
								//Buffer coordinates
								rect.left -= m_ScreenOffsetx;
								rect.right -= m_ScreenOffsetx;
								rect.top -= m_ScreenOffsety;
								rect.bottom -= m_ScreenOffsety;
								rfb::Rect wrect = rfb::Rect(rect).intersect(m_Cliprect);
								if (!wrect.is_empty())
								{
									/*#ifdef _DEBUG
														char			szText[256];
														DWORD error=GetLastError();
														sprintf_s(szText,"CheckRect 222222 ++++++++++++++++ %i %i %i %i  \n",wrect.tl.x,wrect.br.x,wrect.tl.y,wrect.br.y);
														SetLastError(0);
														OutputDebugString(szText);
									#endif*/
									rgn.assign_union(wrect);
									m_lWList.insert(hwnd);
								}
							}
						}
					}
				}
			}

			++iPixelColor; // Next PixelColor in the list
		}
	}
	PixelEngine.ReleaseCapture();
	///////////////////////
	// We coun the number of idle detects
	// after x time, force some timeout
	if (change_found)
	{
#ifdef _DEBUG
		OutputDevMessage("Change found %d", GetTickCount());
#endif
		idle_counter = 0;
	}
	else
	{
#ifdef _DEBUG
		OutputDevMessage("Change idle %d", GetTickCount());
#endif
		idle_counter = idle_counter + 5;
	}
	if (idle_counter > 20)
	{
		//Beep(100,idle_counter);
		Sleep(idle_counter);
	}
	// 250 increased to 500, possible even 1000 will still have a good reaction time
	if (idle_counter > 1000) idle_counter = 1000;
	///////////////////////

	if (fIncCycle)
	{
		m_nGridCycle = (m_nGridCycle + 1) % (PIXEL_BLOCK_SIZE / GRID_OFFSET);
	}
	return returnvalue;
}




// Implementation

vncDesktop::vncDesktop()
{
	m_server = NULL;
	m_thread = NULL;
#ifdef AVILOG
	AviGen = NULL;
#endif
	m_Black_window_active = false;
	m_hwnd = NULL;
	//m_timerid = 0;
	// adzm - 2010-07 - Fix clipboard hangs
	m_settingClipboardViewer = false;
	m_hnextviewer = NULL;
	m_hcursor = NULL;
	m_hOldcursor = NULL; // sf@2002

	m_displaychanged = FALSE;
	m_screensize_changed = FALSE;

	m_hrootdc_Desktop = NULL;
	m_hmemdc = NULL;
	m_membitmap = NULL;

	// adzm - 2010-07 - Fix clipboard hangs
	//m_initialClipBoardSeen = FALSE;

	m_foreground_window = NULL;

	// Vars for Will Dean's DIBsection patch
	m_DIBbits = NULL;
	m_formatmunged = FALSE;

	m_clipboard_active = FALSE;

	m_pollingcycle = 0;

	// Modif sf@2002 - v1.1.0 
	m_lWList.clear();
	m_lGridsList.clear();
	m_nGridCycle = 0;

	// sf@2002 - TextChat - No more used for now
	// m_fTextChatRunning = false;
	// m_pCurrentTextChat = NULL;

	// Modif rdv@2002 - v1.1.x - videodriver
	m_screenCapture = NULL;
	m_ScreenOffsetx = 0;
	m_ScreenOffsety = 0;
	m_hookdriver = false;

	m_screen_in_powersave = 0;

	On_Off_hookdll = false;
	g_Desktop_running = true;
	hUser32 = LoadLibrary("USER32");
	if (hUser32) pbi = (pBlockInput)GetProcAddress(hUser32, "BlockInput");
	no_default_desktop = false;
	DriverWantedSet = false;
	can_be_hooked = false;

	show_all_monitors = true;
	m_bIsInputDisabledByClient = false;
	m_input_desktop = 0;
	m_home_desktop = 0;
	idle_counter = 0;
	trigger_events[0] = CreateEvent(NULL, TRUE, FALSE, "timer");
	trigger_events[1] = CreateEvent(NULL, TRUE, FALSE, "screenupdate");
	trigger_events[2] = CreateEvent(NULL, TRUE, FALSE, "mouseupdate");
	trigger_events[3] = CreateEvent(NULL, TRUE, FALSE, "user1");
	trigger_events[4] = CreateEvent(NULL, TRUE, FALSE, "user2");
	trigger_events[5] = CreateEvent(NULL, TRUE, FALSE, "quit");
	trigger_events[6] = eventPlaceholder1 = CreateEvent(NULL, TRUE, FALSE, "placeholder1");
	trigger_events[7] = eventPlaceholder2 = CreateEvent(NULL, TRUE, FALSE, "placeholder2");
	restart_event = CreateEvent(NULL, TRUE, TRUE, "restart");
	rgnpump.clear();
	lock_region_add = false;
	InitWindowThreadh = NULL;
	old_Blockinput = 2;
	old_Blockinput1 = 2;
	old_Blockinput2 = 2;
	nr_rects = 0;
	iconregion.clear();
	blankmonitorstate = false;

	// JnZn558
	m_current_monitor = MULTI_MON_PRIMARY;
	m_old_monitor = MULTI_MON_PRIMARY;

	sesmsg = NULL;
	sesmsg = new sessionmsg[100];
	for (int i = 0; i < 100; i++)
	{
		sesmsg[i].ID = 9999;
		memset(sesmsg[i].name, 0, 32);
		memset(sesmsg[i].username, 0, 32);
		memset(sesmsg[i].type, 0, 32);
	}
	m_SWOffsetx = 0;
	m_SWOffsety = 0;
}

vncDesktop::~vncDesktop()
{
	strcpy_s(g_hookstring, "");
	vnclog.Print(LL_INTINFO, VNCLOG("~vncDesktop \n"));

	// If we created a thread then here we delete it
	// The thread itself does most of the cleanup
	if (m_thread != NULL)
	{
		StopInitWindowthread();
		vncDesktopThread *thread = (vncDesktopThread*)m_thread;
		int counter = 0;
		while (g_DesktopThread_running != false)
		{
			if (Window() == NULL)
				SetEvent(trigger_events[5]);;
			Sleep(100);
			counter++;
			if (counter > 50)
			{
				vnclog.Print(LL_INTINFO, VNCLOG("Desktop thread running, force close \n"));
				SetEvent(trigger_events[5]);
				break;
			}
		}
		// Join with the desktop handler thread
		void *returnval;
		m_thread->join(&returnval);
		m_thread = NULL;
	}



	// added jeff
	if (m_server) {
		SetBlockInputState(false);
		PreventScreensaver(false);
	}
	// Let's call Shutdown just in case something went wrong...
	Shutdown();
	vnclog.Print(LL_INTINFO, VNCLOG("~vncDesktop Shutdown()\n"));
	// Modif sf@2002
	m_lWList.clear();
	GridsList::iterator iGrid;
	for (iGrid = m_lGridsList.begin(); iGrid != m_lGridsList.end(); iGrid++)
	{
		if (*iGrid)
		{
			// Since we've replaced this:
			// "typedef std::list<RGBPixelList*> GridsList;"
			// with this:
			// "typedef std::list<void*> GridsList;"
			// we must be carefull to avoid memory leaks...
			((RGBPixelList*)(*iGrid))->clear();
			delete ((RGBPixelList*)(*iGrid));
			vnclog.Print(LL_INTWARN, VNCLOG("delete ((RGBPixelList) \n"));
		}
	}
	vnclog.Print(LL_INTINFO, VNCLOG("~vncDesktop m_lGridsList.clear\n"));
	m_lGridsList.clear();
	if (hUser32) FreeLibrary(hUser32);
	g_Desktop_running = false;
	for (int i = 0; i < 6; i++)
		CloseHandle(trigger_events[i]);
	CloseHandle(restart_event);
	CloseHandle(eventPlaceholder1);
	CloseHandle(eventPlaceholder2);
	//problems, sync could be restarted in the little time the desktop thread was still running
	//then this doesn't exist on desktop close and sink window crash
	// Fix E. SAG
	if (InitWindowThreadh)
	{
		vnclog.Print(LL_INTERR, VNCLOG("~vncDesktop:: second request to close InitWindowthread\n"));
		StopInitWindowthread();
	}
	Sleep(1000);//FIX
	if (m_hrootdc_Desktop != NULL)
	{
		if (!ReleaseDC(NULL, m_hrootdc_Desktop))
			vnclog.Print(LL_INTERR, VNCLOG("failed to DeleteDC hrootdc\n"));
		DeleteDC(m_hrootdc_Desktop);
		m_hrootdc_Desktop = NULL;
	}
	if (sesmsg) delete[] sesmsg;
	sesmsg = NULL;
}


// Tell the desktop hooks to grab & update a particular rectangle
void
vncDesktop::UpdateFullScreen()
{
	//Full screen Update
	SetEvent(trigger_events[4]);
}

// Kick the desktop hooks to perform an update
void
vncDesktop::TriggerUpdate()
{
	// Note that we should really lock the update lock here,
	// but there are periodic timer updates anyway, so
	// we don't actually need to.  Something to think about.
	if (m_screenCapture == NULL)
		SetEvent(trigger_events[0]);
	//else
	//	m_screenCapture->Unlock();
}

DWORD
vncDesktop::PreConnectInitBitmap()
{

	show_all_monitors = false;
	m_hrootdc_Desktop = GetDC(NULL);
	if (m_hrootdc_Desktop == NULL) return ERROR_DESKTOP_NO_ROOTDC;

	m_ScreenOffsetx = 0;
	m_ScreenOffsety = 0;
	SetBitmapRectOffsetAndClipRect(0, 0, 640, 640);
	m_hmemdc = CreateCompatibleDC(m_hrootdc_Desktop);
	if (m_hmemdc == NULL) return ERROR_DESKTOP_NO_ROOTDC;

	m_membitmap = CreateCompatibleBitmap(m_hrootdc_Desktop, 640, 640);
	if (m_membitmap == NULL) return ERROR_DESKTOP_NO_COMPATBITMAP;
	memset(&m_bminfo, 0, sizeof(m_bminfo));
	m_bminfo.bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	m_bminfo.bmi.bmiHeader.biBitCount = 32;
	m_bminfo.bmi.bmiHeader.biPlanes = 1;
	m_bminfo.bmi.bmiHeader.biWidth = m_bmrect.br.x;
	m_bminfo.bmi.bmiHeader.biHeight = m_bmrect.br.y;
	m_bminfo.bmi.bmiHeader.biSizeImage = abs((m_bminfo.bmi.bmiHeader.biWidth *m_bminfo.bmi.bmiHeader.biHeight *m_bminfo.bmi.bmiHeader.biBitCount) / 8);
	m_bminfo.bmi.bmiHeader.biHeight = -abs(m_bminfo.bmi.bmiHeader.biHeight);
	m_bminfo.bmi.bmiHeader.biCompression = BI_RGB;
	m_bminfo.truecolour = true;
	m_DIBbits = NULL;
	return 0;
}
DWORD
vncDesktop::PreConnectStartup()
{
	DWORD status;
	if (!InitDesktop())
	{
		vnclog.Print(LL_INTINFO, VNCLOG("InitDesktop Failed\n"));
		return ERROR_DESKTOP_INIT_FAILED;
	}
	m_server->Driver(false);
	m_server->Hook(false);
	m_buffer.VideDriverUsed(false);
	if ((status = PreConnectInitBitmap()) != 0) return status;
	SetPixFormat();
	SetPixShifts();
	StartInitWindowthread();
	if (!m_buffer.SetDesktop(this))return ERROR_DESKTOP_OUT_OF_MEMORY;	
	return 0;
}
// Routine to startup and install all the hooks and stuff
DWORD
vncDesktop::Startup()
{
	if (PreConnect) 
		return PreConnectStartup();
	// Initialise the Desktop object
	DWORD status;
	if (!InitDesktop()) {
		vnclog.Print(LL_INTINFO, VNCLOG("InitDesktop Failed\n"));
		return ERROR_DESKTOP_INIT_FAILED;
	}

	// Modif rdv@2002 - v1.1.x - videodriver
	vnclog.Print(LL_INTINFO, VNCLOG("InitVideo driver Called\n"));
	if (FALSE != DriverWantedSet) {
		m_server->Driver(DriverWanted);
		m_server->Hook(HookWanted);
		DriverWantedSet = FALSE;
	}
	no_default_desktop = false;
	if (m_server->Driver()) {
		vnclog.Print(LL_INTINFO, VNCLOG("Driver option enabled \n"));
		//Enable only the video driver for the Default desktop
		HDESK desktop = GetThreadDesktop(GetCurrentThreadId());
		DWORD dummy;
		char new_name[256];
		if (GetUserObjectInformation(desktop, UOI_NAME, &new_name, 256, &dummy)) {
			if (strcmp(new_name, "Default") == 0 && vncService::InputDesktopSelected() != 2) {
				InitVideoDriver();
				no_default_desktop = false;
			}
			else {
				vnclog.Print(LL_INTINFO, VNCLOG("no default desktop \n"));
				no_default_desktop = true;
			}
		}
	}
	else 
		vnclog.Print(LL_INTINFO, VNCLOG("Driver option disabled \n"));


	if (VideoBuffer())
		vnclog.Print(LL_INTINFO, VNCLOG("Break log\n"));
	if ((status = InitBitmap()) != 0) {
		vnclog.Print(LL_INTINFO, VNCLOG("InitBitmap Failed\n"));
		return status;
	}

	if (!ThunkBitmapInfo()) {
		vnclog.Print(LL_INTINFO, VNCLOG("ThunkBitmapInfo Failed\n"));
		return FALSE;
	}

	if (VideoBuffer()) {
		vnclog.Print(LL_INTINFO, VNCLOG("Removing real Dib buffer and replace by driver communication buffer\n"));
		if (m_membitmap != NULL) {
			DeleteObject(m_membitmap);
			m_membitmap = NULL;
		}
		m_DIBbits = m_screenCapture->getFramebuffer();
		pchanges_buf = m_screenCapture->getChangeBuffer();
		m_buffer.VideDriverUsed(true);
		InvalidateRect(NULL, NULL, TRUE);
	}
	else if ((status = EnableOptimisedBlits()) != 0) {
		vnclog.Print(LL_INTINFO, VNCLOG("EnableOptimisedBlits Failed\n"));
		m_buffer.VideDriverUsed(false);
		return status;
	}
	else 
		m_buffer.VideDriverUsed(false);

	if ((status = SetPixFormat()) != 0) {
		vnclog.Print(LL_INTINFO, VNCLOG("SetPixFormat Failed\n"));
		return status;
	}

	if (!SetPixShifts()) {
		vnclog.Print(LL_INTINFO, VNCLOG("SetPixShift Failed\n"));
		return ERROR_DESKTOP_UNSUPPORTED_PIXEL_FORMAT;
	}

	if (!SetPalette()) {
		vnclog.Print(LL_INTINFO, VNCLOG("SetPalette Failed\n"));
		return ERROR_DESKTOP_NO_PALETTE;
	}

	StartInitWindowthread();

	// Start a timer to handle Polling Mode.  The timer will cause
	// an "idle" event once every 1/10 second, which is necessary if Polling
	// Mode is being used, to cause TriggerUpdate to be called.


	// Initialise the buffer object
	if (!m_buffer.SetDesktop(this))
		return ERROR_DESKTOP_OUT_OF_MEMORY;
#ifdef AVILOG
	if (vnclog.GetVideo()) {
		SYSTEMTIME lt;
		GetLocalTime(&lt);
		char str[MAX_PATH + 32]; // 29 January 2008 jdp 
		_snprintf_s(str, sizeof str, "%02d_%02d_%02d_%02d_%02d", lt.wMonth, lt.wDay, lt.wHour, lt.wMinute, lt.wSecond);
		strcat_s(str, "_vnc.avi");
		AviGen = new CAVIGenerator(str, "c:\\temp", &m_bminfo.bmi.bmiHeader, 5);
		HRESULT hr;
		hr = AviGen->InitEngine();
		if (FAILED(hr))
		{
			AviGen->ReleaseEngine();
			delete AviGen;
			AviGen = NULL;
		}

	}
#endif
	// Everything is ok, so return success
	return 0;
}

// Routine to shutdown all the hooks and stuff
BOOL
vncDesktop::Shutdown()
{
#ifdef AVILOG
	if (AviGen)
	{
		AviGen->ReleaseEngine();
		delete AviGen;
		AviGen = NULL;
	}
#endif	
	ShutdownInitWindowthread();

	// Now free all the bitmap stuff
	if (m_hrootdc_Desktop != NULL)
	{
		if (!ReleaseDC(NULL, m_hrootdc_Desktop))
			vnclog.Print(LL_INTERR, VNCLOG("failed to DeleteDC hrootdc\n"));
		DeleteDC(m_hrootdc_Desktop);
		m_hrootdc_Desktop = NULL;
	}
	if (m_hmemdc != NULL)
	{
		// Release our device context
		if (!DeleteDC(m_hmemdc))
		{
			vnclog.Print(LL_INTERR, VNCLOG("failed to DeleteDC hmemdc\n"));
		}
		m_hmemdc = NULL;
	}
	if (m_membitmap != NULL)
	{
		// Release the custom bitmap, if any
		if (!DeleteObject(m_membitmap))
		{
			vnclog.Print(LL_INTERR, VNCLOG("failed to DeleteObject\n"));
		}
		m_membitmap = NULL;
	}

	m_DIBbits = NULL;

	if (m_hcursor)
	{
		DeleteObject(m_hcursor);
		m_hcursor = NULL;
	}
	if (m_hOldcursor)
	{
		DeleteObject(m_hOldcursor);
		m_hOldcursor = NULL;
	}
	// Modif rdv@2002 - v1.1.x - videodriver
	ShutdownVideoDriver();

	if (m_home_desktop)
		vncService::SelectHDESK(m_home_desktop);
	if (m_input_desktop)
	{
		if (!CloseDesktop(m_input_desktop))
			vnclog.Print(LL_INTERR, VNCLOG("failed to close desktop\n"));
		m_input_desktop = 0;
	}
	GridsList::iterator iGrid;
	for (iGrid = m_lGridsList.begin(); iGrid != m_lGridsList.end(); iGrid++)
	{
		if (*iGrid)
		{
			// Since we've replaced this:
			// "typedef std::list<RGBPixelList*> GridsList;"
			// with this:
			// "typedef std::list<void*> GridsList;"
			// we must be carefull to avoid memory leaks...
			((RGBPixelList*)(*iGrid))->clear();
			delete ((RGBPixelList*)(*iGrid));
			vnclog.Print(LL_INTWARN, VNCLOG("delete ((RGBPixelList) \n"));
		}
	}
	m_lGridsList.clear();
	m_foreground_window_rect.clear();
	trigger_events[6] = eventPlaceholder1;
	trigger_events[7] = eventPlaceholder2;
	return TRUE;
}

// Routine to ensure we're on the correct NT desktop

BOOL
vncDesktop::InitDesktop()
{
	int result = vncService::InputDesktopSelected();
	if (result == 1 || result == 2)
		return TRUE;
	vnclog.Print(LL_INTINFO, VNCLOG("InitDesktop...\n"));
	return vncService::SelectDesktop(NULL, &m_input_desktop);
}

// Routine used to close the screen saver, if it's active...

BOOL CALLBACK
KillScreenSaverFunc(HWND hwnd, LPARAM lParam)
{
	char buffer[256];

	// - ONLY try to close Screen-saver windows!!!
	if ((GetClassName(hwnd, buffer, 256) != 0) &&
		(strcmp(buffer, "WindowsScreenSaverClass") == 0))
		PostMessage(hwnd, WM_CLOSE, 0, 0);
	return TRUE;
}

void
vncDesktop::KillScreenSaver()
{
	OSVERSIONINFO osversioninfo;
	osversioninfo.dwOSVersionInfoSize = sizeof(osversioninfo);

	// Get the current OS version
	if (!GetVersionEx(&osversioninfo))
		return;

	vnclog.Print(LL_INTINFO, VNCLOG("KillScreenSaver...\n"));

	// How to kill the screen saver depends on the OS
	switch (osversioninfo.dwPlatformId)
	{
	case VER_PLATFORM_WIN32_WINDOWS:
	{
		// Windows 95

		// Fidn the ScreenSaverClass window
		HWND hsswnd = FindWindow("WindowsScreenSaverClass", NULL);
		if (hsswnd != NULL)
			PostMessage(hsswnd, WM_CLOSE, 0, 0);
		break;
	}
	case VER_PLATFORM_WIN32_NT:
	{
		// Windows NT

		// Find the screensaver desktop
		HDESK hDesk = OpenDesktop(
			"Screen-saver",
			0,
			FALSE,
			DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS
		);
		if (hDesk != NULL)
		{
			vnclog.Print(LL_INTINFO, VNCLOG("Killing ScreenSaver\n"));

			// Close all windows on the screen saver desktop
			EnumDesktopWindows(hDesk, (WNDENUMPROC)&KillScreenSaverFunc, 0);
			CloseDesktop(hDesk);
			// Pause long enough for the screen-saver to close
			//Sleep(2000);
			// Reset the screen saver so it can run again
			SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, TRUE, 0, SPIF_SENDWININICHANGE);
		}
		break;
	}
	}
}

DWORD
vncDesktop::InitBitmap()
{
	// Get the device context for the whole screen and find it's size
	show_all_monitors = false;
	DriverType = NONE;
	if (VideoBuffer()) {
		LPSTR driverName = "mv video hook driver2";
		BOOL DriverFound;
		DEVMODE devmode;
		FillMemory(&devmode, sizeof(DEVMODE), 0);
		devmode.dmSize = sizeof(DEVMODE);
		devmode.dmDriverExtra = 0;
		BOOL change = EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &devmode);
		devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
		HMODULE hUser32 = LoadLibrary("USER32");
		LPSTR deviceName = NULL;
		DISPLAY_DEVICE dd;
		ZeroMemory(&dd, sizeof(dd));
		dd.cb = sizeof(dd);
		devmode.dmDeviceName[0] = '\0';
		INT devNum = 0;
		BOOL result;
		DriverFound = false;
		while (result = EnumDisplayDevicesA(NULL, devNum, &dd, 0)) {
			if (strcmp((const char *)&dd.DeviceString[0], driverName) == 0) {
				DriverFound = true;
				break;
			}
			devNum++;
		}
		if (DriverFound) {
			deviceName = (LPSTR)&dd.DeviceName[0];
			//JnZn558 
			if (m_hrootdc_Desktop != NULL) {
				if (!ReleaseDC(NULL, m_hrootdc_Desktop))
					vnclog.Print(LL_INTERR, VNCLOG("failed to DeleteDC hrootdc\n"));
				DeleteDC(m_hrootdc_Desktop);
				m_hrootdc_Desktop = NULL;
			}
			m_hrootdc_Desktop = CreateDC("DISPLAY", deviceName, NULL, NULL);
			//m_hrootdc = GetDC(NULL);
			BOOL temp_para = EnumDisplaySettings(deviceName, ENUM_CURRENT_SETTINGS, &devmode);
			if (m_hrootdc_Desktop) 
				DriverType = MIRROR;
		}
	}//VIDEOBUFFER

	if (m_hrootdc_Desktop == NULL) {
		vnclog.Print(LL_INTERR, VNCLOG("No driver used \n"));
		//Multi-Monitor changes
		m_hrootdc_Desktop = GetDC(NULL);
		if (m_hrootdc_Desktop == NULL) {
			vnclog.Print(LL_INTERR, VNCLOG("Failed m_rootdc \n"));
			return ERROR_DESKTOP_NO_ROOTDC;
		}

	}

	Checkmonitors();
	requested_all_monitor = m_buffer.IsAllMonitors();
    if (requested_all_monitor && nr_monitors > 1)
    {
        show_all_monitors = true;
        m_current_monitor = MULTI_MON_ALL;
        m_old_monitor = m_current_monitor;
    }

	if (VideoBuffer())
		SetBitmapRectOffsetAndClipRect(mymonitor[MULTI_MON_ALL].offsetx, mymonitor[MULTI_MON_ALL].offsety, mymonitor[MULTI_MON_ALL].Width, mymonitor[MULTI_MON_ALL].Height);
	else if (show_all_monitors) 
		SetBitmapRectOffsetAndClipRect(0, 0, mymonitor[MULTI_MON_ALL].Width, mymonitor[MULTI_MON_ALL].Height);
	else
		SetBitmapRectOffsetAndClipRect(0, 0, mymonitor[MULTI_MON_PRIMARY].Width, mymonitor[MULTI_MON_PRIMARY].Height);
	

	vnclog.Print(LL_INTINFO, VNCLOG("bitmap dimensions are %d x %d\n"), m_bmrect.br.x, m_bmrect.br.y);

	// Create a compatible memory DC
	m_hmemdc = CreateCompatibleDC(m_hrootdc_Desktop);
	if (m_hmemdc == NULL) {
		vnclog.Print(LL_INTERR, VNCLOG("failed to create compatibleDC(%d)\n"), GetLastError());
		return ERROR_DESKTOP_NO_ROOTDC;
	}

	// Check that the device capabilities are ok
	if ((GetDeviceCaps(m_hrootdc_Desktop, RASTERCAPS) & RC_BITBLT) == 0)
	{
		MessageBoxSecure(
			NULL,
			"vncDesktop : root device doesn't support BitBlt\n"
			"WinVNC cannot be used with this graphic device driver",
			szAppName,
			MB_ICONSTOP | MB_OK
		);
		return ERROR_DESKTOP_NO_BITBLT;
	}
	if ((GetDeviceCaps(m_hmemdc, RASTERCAPS) & RC_DI_BITMAP) == 0)
	{
		MessageBoxSecure(
			NULL,
			"vncDesktop : memory device doesn't support GetDIBits\n"
			"WinVNC cannot be used with this graphics device driver",
			szAppName,
			MB_ICONSTOP | MB_OK
		);
		return ERROR_DESKTOP_NO_GETDIBITS;
	}

	// Create the bitmap to be compatible with the ROOT DC!!!
	m_membitmap = CreateCompatibleBitmap(m_hrootdc_Desktop, 1, 1);
	if (m_membitmap == NULL) {
		vnclog.Print(LL_INTERR, VNCLOG("failed to create memory bitmap(%d)\n"), GetLastError());
		return ERROR_DESKTOP_NO_COMPATBITMAP;
	}
	vnclog.Print(LL_INTINFO, VNCLOG("created memory bitmap\n"));

	// Get the bitmap's format and colour details
	int result;
	memset(&m_bminfo, 0, sizeof(m_bminfo));
	m_bminfo.bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	m_bminfo.bmi.bmiHeader.biBitCount = 0;
	result = ::GetDIBits(m_hmemdc, m_membitmap, 0, 1, NULL, &m_bminfo.bmi, DIB_RGB_COLORS);
	if (result == 0) {
		vnclog.Print(LL_INTERR, VNCLOG("unable to get display format\n"));
		return ERROR_DESKTOP_NO_DISPLAYFORMAT;
	}
	//needed, no dubble, need to be executed 2 times
	result = ::GetDIBits(m_hmemdc, m_membitmap, 0, 1, NULL, &m_bminfo.bmi, DIB_RGB_COLORS);
	if (result == 0) {
		vnclog.Print(LL_INTERR, VNCLOG("unable to get display colour info\n"));
		return ERROR_DESKTOP_NO_DISPLAYFORMAT;
	}
	vnclog.Print(LL_INTINFO, VNCLOG("got bitmap format\n"));

	// Henceforth we want to use a top-down scanning representation
	m_bminfo.bmi.bmiHeader.biWidth = m_bmrect.br.x;
	m_bminfo.bmi.bmiHeader.biHeight = m_bmrect.br.y;
	m_bminfo.bmi.bmiHeader.biSizeImage = abs((m_bminfo.bmi.bmiHeader.biWidth *
		m_bminfo.bmi.bmiHeader.biHeight *
		m_bminfo.bmi.bmiHeader.biBitCount) / 8);
	m_bminfo.bmi.bmiHeader.biHeight = -abs(m_bminfo.bmi.bmiHeader.biHeight);

	// Is the bitmap palette-based or truecolour?
	m_bminfo.truecolour = (GetDeviceCaps(m_hmemdc, RASTERCAPS) & RC_PALETTE) == 0;
	InvalidateRect(NULL, NULL, TRUE);
	return 0;
}

BOOL
vncDesktop::ThunkBitmapInfo()
{
	// If we leave the pixel format intact, the blist can be optimised (Will Dean's patch)
	m_formatmunged = FALSE;

	// HACK ***.  Optimised blits don't work with palette-based displays, yet
	if (!m_bminfo.truecolour) {
		m_formatmunged = TRUE;
	}

	// Attempt to force the actual format into one we can handle
	// We can handle 8-bit-palette and 16/32-bit-truecolour modes
	switch (m_bminfo.bmi.bmiHeader.biBitCount)
	{
	case 1:
	case 4:
		vnclog.Print(LL_INTINFO, VNCLOG("DBG:used/bits/planes/comp/size = %d/%d/%d/%d/%d\n"),
			(int)m_bminfo.bmi.bmiHeader.biClrUsed,
			(int)m_bminfo.bmi.bmiHeader.biBitCount,
			(int)m_bminfo.bmi.bmiHeader.biPlanes,
			(int)m_bminfo.bmi.bmiHeader.biCompression,
			(int)m_bminfo.bmi.bmiHeader.biSizeImage);

		// Correct the BITMAPINFO header to the format we actually want
		m_bminfo.bmi.bmiHeader.biClrUsed = 0;
		m_bminfo.bmi.bmiHeader.biPlanes = 1;
		m_bminfo.bmi.bmiHeader.biCompression = BI_RGB;
		m_bminfo.bmi.bmiHeader.biBitCount = 8;
		m_bminfo.bmi.bmiHeader.biSizeImage =
			abs((m_bminfo.bmi.bmiHeader.biWidth *
				m_bminfo.bmi.bmiHeader.biHeight *
				m_bminfo.bmi.bmiHeader.biBitCount) / 8);
		m_bminfo.bmi.bmiHeader.biClrImportant = 0;
		m_bminfo.truecolour = FALSE;

		// Display format is non-VNC compatible - use the slow blit method
		m_formatmunged = TRUE;
		break;
	case 24:
		// Update the bitmapinfo header
		m_bminfo.bmi.bmiHeader.biBitCount = 32;
		m_bminfo.bmi.bmiHeader.biPlanes = 1;
		m_bminfo.bmi.bmiHeader.biCompression = BI_RGB;
		m_bminfo.bmi.bmiHeader.biSizeImage =
			abs((m_bminfo.bmi.bmiHeader.biWidth *
				m_bminfo.bmi.bmiHeader.biHeight *
				m_bminfo.bmi.bmiHeader.biBitCount) / 8);
		// Display format is non-VNC compatible - use the slow blit method
		m_formatmunged = TRUE;
		break;
	}

	return TRUE;
}

DWORD
vncDesktop::SetPixFormat()
{
	// If we are using a memory bitmap then check how many planes it uses
	 // The VNC code can only handle formats with a single plane (CHUNKY pixels)
	if (!m_DIBbits) {
		vnclog.Print(LL_INTINFO, VNCLOG("DBG:display context has %d planes!\n"),
			GetDeviceCaps(m_hrootdc_Desktop, PLANES));
		vnclog.Print(LL_INTINFO, VNCLOG("DBG:memory context has %d planes!\n"),
			GetDeviceCaps(m_hmemdc, PLANES));
		if (GetDeviceCaps(m_hmemdc, PLANES) != 1)
		{
			MessageBoxSecure(
				NULL,
				"vncDesktop : current display is PLANAR, not CHUNKY!\n"
				"WinVNC cannot be used with this graphics device driver",
				szAppName,
				MB_ICONSTOP | MB_OK
			);
			return ERROR_DESKTOP_UNSUPPORTED_PIXEL_ORGANIZATION;
		}
	}


	// Examine the bitmapinfo structure to obtain the current pixel format
	m_scrinfo.format.trueColour = m_bminfo.truecolour;
	m_scrinfo.format.bigEndian = 0;

	// Set up the native buffer width, height and format
	/* JnZn558
	m_scrinfo.framebufferWidth = (CARD16) (m_bmrect.br.x - m_bmrect.tl.x);		// Swap endian before actually sending
	m_scrinfo.framebufferHeight = (CARD16) (m_bmrect.br.y - m_bmrect.tl.y);	// Swap endian before actually sending
	*/

	m_scrinfo.framebufferWidth = (CARD16)(m_bmrect.br.x - ((m_bmrect.tl.x < 0) ? 0 : m_bmrect.tl.x));		// Swap endian before actually sending
	m_scrinfo.framebufferHeight = (CARD16)(m_bmrect.br.y - ((m_bmrect.tl.y < 0) ? 0 : m_bmrect.tl.y));	// Swap endian before actually sending
	m_scrinfo.format.bitsPerPixel = (CARD8)m_bminfo.bmi.bmiHeader.biBitCount;
	m_scrinfo.format.depth = (CARD8)m_bminfo.bmi.bmiHeader.biBitCount;

	// Calculate the number of bytes per row
	m_bytesPerRow = m_scrinfo.framebufferWidth * m_scrinfo.format.bitsPerPixel / 8;

	return 0;
}

BOOL
vncDesktop::SetPixShifts()
{
	// Sort out the colour shifts, etc.
	DWORD redMask = 0, blueMask = 0, greenMask = 0;

	switch (m_bminfo.bmi.bmiHeader.biBitCount)
	{
	case 16:
		// Standard 16-bit display
		if (m_bminfo.bmi.bmiHeader.biCompression == BI_RGB)
		{
			// each word single pixel 5-5-5
			redMask = 0x7c00; greenMask = 0x03e0; blueMask = 0x001f;
		}
		else
		{
			if (m_bminfo.bmi.bmiHeader.biCompression == BI_BITFIELDS)
			{
				redMask = *(DWORD *)&m_bminfo.bmi.bmiColors[0];
				greenMask = *(DWORD *)&m_bminfo.bmi.bmiColors[1];
				blueMask = *(DWORD *)&m_bminfo.bmi.bmiColors[2];
			}
		}
		//if (m_screenCapture) redMask = 0x7c00; greenMask = 0x03e0; blueMask = 0x001f;
		break;


	case 32:
		// Standard 24/32 bit displays
		if (m_bminfo.bmi.bmiHeader.biCompression == BI_RGB)
		{
			redMask = 0xff0000; greenMask = 0xff00; blueMask = 0x00ff;
		}
		else
		{
			if (m_bminfo.bmi.bmiHeader.biCompression == BI_BITFIELDS)
			{
				redMask = *(DWORD *)&m_bminfo.bmi.bmiColors[0];
				greenMask = *(DWORD *)&m_bminfo.bmi.bmiColors[1];
				blueMask = *(DWORD *)&m_bminfo.bmi.bmiColors[2];
			}
		}
		break;

	default:
		// Other pixel formats are only valid if they're palette-based
		if (m_bminfo.truecolour)
		{
			vnclog.Print(LL_INTERR, "unsupported truecolour pixel format for setpixshifts\n");
			return FALSE;
		}
		return TRUE;
	}

	// Convert the data we just retrieved
	MaskToMaxAndShift(redMask, m_scrinfo.format.redMax, m_scrinfo.format.redShift);
	MaskToMaxAndShift(greenMask, m_scrinfo.format.greenMax, m_scrinfo.format.greenShift);
	MaskToMaxAndShift(blueMask, m_scrinfo.format.blueMax, m_scrinfo.format.blueShift);

	return TRUE;
}

BOOL
vncDesktop::SetPalette()
{
	// Lock the current display palette into the memory DC we're holding
	// *** CHECK THIS FOR LEAKS!
	if (VideoBuffer())
		return TRUE;
	if (!m_bminfo.truecolour)
	{
		if (!m_DIBbits)
		{
			// - Handle the palette for a non DIB-Section

			LOGPALETTE *palette;
			UINT size = sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * 256);

			palette = (LOGPALETTE *) new char[size];
			if (palette == NULL) {
				vnclog.Print(LL_INTERR, VNCLOG("unable to allocate logical palette\n"));
				return FALSE;
			}

			// Initialise the structure
			palette->palVersion = 0x300;
			palette->palNumEntries = 256;

			// Get the system colours
			if (GetSystemPaletteEntries(m_hrootdc_Desktop,
				0, 256, palette->palPalEntry) == 0)
			{
				vnclog.Print(LL_INTERR, VNCLOG("unable to get system palette entries\n"));
				delete[] palette;
				return FALSE;
			}

			// Create a palette from those
			HPALETTE pal = CreatePalette(palette);
			if (pal == NULL)
			{
				vnclog.Print(LL_INTERR, VNCLOG("unable to create HPALETTE\n"));
				delete[] palette;
				return FALSE;
			}

			// Select the palette into our memory DC
			HPALETTE oldpalette = SelectPalette(m_hmemdc, pal, FALSE);
			if (oldpalette == NULL)
			{
				vnclog.Print(LL_INTERR, VNCLOG("unable to select() HPALETTE\n"));
				delete[] palette;
				DeleteObject(pal);
				return FALSE;
			}

			// Worked, so realise the palette
			if (RealizePalette(m_hmemdc) == GDI_ERROR)
				vnclog.Print(LL_INTWARN, VNCLOG("warning - failed to RealizePalette\n"));

			// It worked!
			delete[] palette;
			DeleteObject(oldpalette);

			vnclog.Print(LL_INTINFO, VNCLOG("initialised palette OK\n"));
			return TRUE;
		}
		else
		{
			// - Handle a DIB-Section's palette

			// - Fetch the system palette for the framebuffer
			PALETTEENTRY syspalette[256];
			UINT entries = ::GetSystemPaletteEntries(m_hrootdc_Desktop, 0, 256, syspalette);
			vnclog.Print(LL_INTERR, VNCLOG("framebuffer has %u palette entries"), entries);

			// - Store it and convert it to RGBQUAD format
			RGBQUAD dibpalette[256];
			unsigned int i;
			for (i = 0; i < entries; i++) {
				dibpalette[i].rgbRed = syspalette[i].peRed;
				dibpalette[i].rgbGreen = syspalette[i].peGreen;
				dibpalette[i].rgbBlue = syspalette[i].peBlue;
				dibpalette[i].rgbReserved = 0;
			}

			// - Set the rest of the palette to something nasty but usable
			for (i = entries; i < 256; i++) {
				dibpalette[i].rgbRed = i % 2 ? 255 : 0;
				dibpalette[i].rgbGreen = i / 2 % 2 ? 255 : 0;
				dibpalette[i].rgbBlue = i / 4 % 2 ? 255 : 0;
				dibpalette[i].rgbReserved = 0;
			}

			// - Update the DIB section to use the same palette
			HDC bitmapDC = ::CreateCompatibleDC(m_hrootdc_Desktop);
			if (!bitmapDC) {
				vnclog.Print(LL_INTERR, VNCLOG("unable to create temporary DC"), GetLastError());
				return FALSE;
			}
			HBITMAP old_bitmap = (HBITMAP)::SelectObject(bitmapDC, m_membitmap);
			if (!old_bitmap) {
				vnclog.Print(LL_INTERR, VNCLOG("unable to select DIB section into temporary DC"), GetLastError());
				DeleteDC(bitmapDC);
				return FALSE;
			}
			UINT entries_set = ::SetDIBColorTable(bitmapDC, 0, 256, dibpalette);
			if (entries_set == 0) {
				vnclog.Print(LL_INTERR, VNCLOG("unable to set DIB section palette"), GetLastError());
				//				return FALSE;
			}
			if (!::SelectObject(bitmapDC, old_bitmap)) {
				vnclog.Print(LL_INTERR, VNCLOG("unable to restore temporary DC bitmap"), GetLastError());
				DeleteObject(old_bitmap);
				DeleteDC(bitmapDC);
				return FALSE;
			}
			DeleteObject(old_bitmap);
			DeleteDC(bitmapDC);
			return TRUE;
		}
	}

	// Not a palette based local screen - forget it!
	vnclog.Print(LL_INTERR, VNCLOG("no palette data for truecolour display\n"));
	return TRUE;
}
////////////////////////////////////////////////////////////////////////////////
DWORD
vncDesktop::EnableOptimisedBlits()
{
	vnclog.Print(LL_INTINFO, VNCLOG("attempting to enable DIBsection blits\n"));

	// Create a new DIB section
	//HBITMAP tempbitmap=NULL;
	HBITMAP tempbitmap = CreateDIBSection(m_hmemdc, &m_bminfo.bmi, DIB_RGB_COLORS, &m_DIBbits, NULL, 0);
	if (tempbitmap == NULL) {
		vnclog.Print(LL_INTINFO, VNCLOG("failed to build DIB section - reverting to slow blits\n"));
		m_DIBbits = NULL;
		tempbitmap = CreateCompatibleBitmap(m_hrootdc_Desktop, m_bmrect.br.x, m_bmrect.br.y);
		if (tempbitmap == NULL) {
			vnclog.Print(LL_INTERR, VNCLOG("failed to create memory bitmap(%d)\n"), GetLastError());
			return ERROR_DESKTOP_NO_DIBSECTION_OR_COMPATBITMAP;
		}
		else
			vnclog.Print(LL_INTINFO, VNCLOG("enabled slow blits OK\n"));
	}
	else
		vnclog.Print(LL_INTINFO, VNCLOG("enabled fast DIBsection blits OK\n"));

	// Delete the old memory bitmap
	if (m_membitmap != NULL) {
		DeleteObject(m_membitmap);
		m_membitmap = NULL;
	}

	// Replace old membitmap with DIB section
	m_membitmap = tempbitmap;
	return 0;
}

DWORD
vncDesktop::Init(vncServer *server)
{
	vnclog.Print(LL_INTINFO, VNCLOG("initialising desktop handler\n"));

	// Save the server pointer
	m_server = server;
	// Load in the arrow cursor
	m_hdefcursor = LoadCursor(NULL, IDC_ARROW);
	m_hcursor = m_hdefcursor;
	m_hOldcursor = m_hdefcursor; //sf@2002

	// Spawn a thread to handle that window's message queue
	vncDesktopThread *thread = new vncDesktopThread;
	if (thread == NULL) {
		vnclog.Print(LL_INTERR, VNCLOG("failed to start hook thread\n"));
		return ERROR_DESKTOP_NO_DESKTOPTHREAD;
	}
	m_thread = thread;
	return thread->Init(this, m_server);
}

int
vncDesktop::ScreenBuffSize()
{
	return m_scrinfo.format.bitsPerPixel / 8 *
		m_scrinfo.framebufferWidth *
		m_scrinfo.framebufferHeight;
}

void
vncDesktop::FillDisplayInfo(rfbServerInitMsg *scrinfo)
{
	memcpy(scrinfo, &m_scrinfo, sz_rfbServerInitMsg);
}


void
vncDesktop::WriteMessageOnScreen(char * tt, BYTE *scrBuff, UINT scrBuffSize)
{
	// Select the memory bitmap into the memory DC
	RECT rect;
	SetRect(&rect, 0, 0, 300, 120);
	COLORREF bgcol = RGB(0xff, 0x00, 0x00);
	COLORREF oldtxtcol = SetTextColor(m_hmemdc, RGB(0, 0, 254));
	COLORREF oldbgcol = SetBkColor(m_hmemdc, bgcol);
	HBRUSH backgroundBrush = CreateSolidBrush(bgcol);

	HBITMAP oldbitmap;
	if ((oldbitmap = (HBITMAP)SelectObject(m_hmemdc, m_membitmap)) == NULL)
		return;

	FillRect(m_hmemdc, &rect, backgroundBrush);
	DrawText(m_hmemdc, tt, (int)strlen(tt), &rect, DT_CENTER | DT_VCENTER);

	SetBkColor(m_hmemdc, oldbgcol);
	SetTextColor(m_hmemdc, oldtxtcol);
	SelectObject(m_hmemdc, oldbitmap);
	DeleteObject(backgroundBrush);

	CopyToBuffer(rect, scrBuff, scrBuffSize);
}

#include "wtsapi32.h"
char mytext22[1024] = "";
DWORD GetCurrentConsoleSessionID();

void
vncDesktop::WriteMessageOnScreenPreConnect(BYTE *scrBuff, UINT scrBuffSize)
{
	typedef BOOL(WINAPI *pfnWTSEnumerateSessions)(HANDLE, DWORD, DWORD, PWTS_SESSION_INFO*, DWORD*);
	typedef VOID(WINAPI *pfnWTSFreeMemory)(PVOID);
	typedef BOOL(WINAPI * _WTSQUERYSESSIONINFORMATION)(HANDLE hServer, DWORD SessionId, WTS_INFO_CLASS WTSInfoClass, LPTSTR *ppBuffer, DWORD *pBytesReturned);
	helper::DynamicFn<pfnWTSEnumerateSessions> pWTSEnumerateSessions("wtsapi32", "WTSEnumerateSessionsA");
	helper::DynamicFn<pfnWTSFreeMemory> pWTSFreeMemory("wtsapi32", "WTSFreeMemory");

	aantal_session = 0;
	//Current Console session
	DWORD SessionId = 0xFFFFFFFF;
	int coutersessionid = 0;
	while (SessionId == 0xFFFFFFFF)
	{
		SessionId = GetCurrentConsoleSessionID();
		Sleep(1000);
		coutersessionid++;
		if (coutersessionid > 10) break;
	}
	if (SessionId != 0 && SessionId != 0xFFFFFFFF)
	{
		sesmsg[0].ID = SessionId;
		memset(sesmsg[0].type, 0, 32);
		memset(sesmsg[0].name, 0, 32);
		strcpy_s(sesmsg[0].type, 32, "Not Active");
		strcpy_s(sesmsg[0].name, 32, "Console");
		memset(sesmsg[aantal_session].username, 0, 32);
		strcpy_s(sesmsg[aantal_session].username, 32, "Current Console");
		HMODULE handle = LoadLibrary("WTSAPI32.DLL");
		_WTSQUERYSESSIONINFORMATION pFunc;
		LPTSTR  ppBuffer = NULL;    DWORD   pBytesReturned = 0;
		if (handle)
		{
			pFunc = (_WTSQUERYSESSIONINFORMATION)GetProcAddress(handle, "WTSQuerySessionInformationA");
			if (pFunc(WTS_CURRENT_SERVER_HANDLE, sesmsg[0].ID, WTSUserName, &ppBuffer, &pBytesReturned))
			{
				strcpy_s(sesmsg[0].username, 32, ppBuffer);
				strcpy_s(sesmsg[0].type, 32, "Active");
			}
		}
		aantal_session++;

		WTS_SESSION_INFO *pSessions = 0;
		DWORD   nSessions(0);
		if (pWTSEnumerateSessions.isValid() && pWTSFreeMemory.isValid())
		{
			if ((*pWTSEnumerateSessions)(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessions, &nSessions))
			{
				for (DWORD i(0); i < nSessions; ++i)
				{
					if (((pSessions[i].State == WTSActive || pSessions[i].State == WTSShadow || pSessions[i].State == WTSConnectQuery)) && (_stricmp(pSessions[i].pWinStationName, "Console") != 0))
					{
						sesmsg[aantal_session].ID = pSessions[i].SessionId;
						memset(sesmsg[aantal_session].type, 0, 32);
						memset(sesmsg[aantal_session].name, 0, 32);
						strcpy_s(sesmsg[aantal_session].type, 32, "Active");
						strcpy_s(sesmsg[aantal_session].name, 32, pSessions[i].pWinStationName);
						HMODULE handle = LoadLibrary("WTSAPI32.DLL");
						_WTSQUERYSESSIONINFORMATION pFunc;
						LPTSTR  ppBuffer = NULL;    DWORD   pBytesReturned = 0;
						if (handle)
						{
							pFunc = (_WTSQUERYSESSIONINFORMATION)GetProcAddress(handle, "WTSQuerySessionInformationA");
							if (pFunc(WTS_CURRENT_SERVER_HANDLE, pSessions[i].SessionId, WTSUserName, &ppBuffer, &pBytesReturned))
							{
								memset(sesmsg[aantal_session].username, 0, 32);
								strcpy_s(sesmsg[aantal_session].username, 32, ppBuffer);
							}
						}
						aantal_session++;
						if (handle) FreeLibrary(handle);
						(*pWTSFreeMemory)(ppBuffer);
					}
				}
				(*pWTSFreeMemory)(pSessions);
			}
		}
	}
	else
	{
		WTS_SESSION_INFO *pSessions = 0;
		DWORD   nSessions(0);
		if (pWTSEnumerateSessions.isValid() && pWTSFreeMemory.isValid())
		{
			if ((*pWTSEnumerateSessions)(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessions, &nSessions))
			{
				for (DWORD i(0); i < nSessions; ++i)
				{
					if (((pSessions[i].State == WTSActive || pSessions[i].State == WTSShadow || pSessions[i].State == WTSConnectQuery)))
					{
						sesmsg[aantal_session].ID = pSessions[i].SessionId;
						memset(sesmsg[aantal_session].type, 0, 32);
						memset(sesmsg[aantal_session].name, 0, 32);
						strcpy_s(sesmsg[aantal_session].type, 32, "Active");
						strcpy_s(sesmsg[aantal_session].name, 32, pSessions[i].pWinStationName);
						HMODULE handle = LoadLibrary("WTSAPI32.DLL");
						_WTSQUERYSESSIONINFORMATION pFunc;
						LPTSTR  ppBuffer = NULL;    DWORD   pBytesReturned = 0;
						if (handle)
						{
							pFunc = (_WTSQUERYSESSIONINFORMATION)GetProcAddress(handle, "WTSQuerySessionInformationA");
							if (pFunc(WTS_CURRENT_SERVER_HANDLE, pSessions[i].SessionId, WTSUserName, &ppBuffer, &pBytesReturned))
							{
								memset(sesmsg[aantal_session].username, 0, 32);
								strcpy_s(sesmsg[aantal_session].username, 32, ppBuffer);
							}
						}
						aantal_session++;
						if (handle) FreeLibrary(handle);
						(*pWTSFreeMemory)(ppBuffer);
					}
				}
				(*pWTSFreeMemory)(pSessions);
			}
		}
	}
	////////////////////////////////////////////////
	char bigstring[128];
	char menustring[12800];
	strcpy_s(menustring, "");

	for (DWORD i(0); i < (DWORD)aantal_session; ++i)
	{
		if (sesmsg[i].ID != 65536 && sesmsg[i].ID != 0)
		{
			sprintf_s(bigstring, "%c) session%i %s user=%s  status=%s", static_cast<char>(97 + i), static_cast<int>(sesmsg[i].ID), sesmsg[i].name, sesmsg[i].username, sesmsg[i].type);
			strcat_s(menustring, bigstring);
			strcat_s(menustring, "\n");
		}
	}
	strcat_s(menustring, "\n");
	strcat_s(menustring, "\n");
	strcat_s(menustring, "Enter session to clone: \n");
	////////////////////////////////////////////////
		// Select the memory bitmap into the memory DC
	RECT rect;
	SetRect(&rect, 0, 0, 640, 640);
	COLORREF bgcol = RGB(0x00, 0x00, 0xff);
	COLORREF oldtxtcol = SetTextColor(m_hmemdc, RGB(254, 254, 254));
	COLORREF oldbgcol = SetBkColor(m_hmemdc, bgcol);
	HBRUSH backgroundBrush = CreateSolidBrush(bgcol);

	HBITMAP oldbitmap;
	if ((oldbitmap = (HBITMAP)SelectObject(m_hmemdc, m_membitmap)) == NULL)
		return;

	FillRect(m_hmemdc, &rect, backgroundBrush);

	HFONT hFont, hOldFont;
	SetRect(&rect, 0, 10, 640, 640);
    char *tout = "UVNC experimental server 1.3.2 pre-connect window \n";
	DrawText(m_hmemdc, tout, (int)strlen(tout), &rect, DT_CENTER);


	if (strlen(mytext22) == 0)getinfo(mytext22);

	SetRect(&rect, 30, 50, 640, 640);
	hFont = (HFONT)GetStockObject(ANSI_FIXED_FONT);
	if ((hOldFont = (HFONT)SelectObject(m_hmemdc, hFont)))
	{
		DrawText(m_hmemdc, mytext22, (int)strlen(mytext22), &rect, DT_LEFT | DT_WORDBREAK);
		SelectObject(m_hmemdc, hOldFont);

	}

	SetRect(&rect, 30, 180, 640, 640);
	hFont = (HFONT)GetStockObject(ANSI_FIXED_FONT);
	if ((hOldFont = (HFONT)SelectObject(m_hmemdc, hFont)))
	{
		DrawText(m_hmemdc, menustring, (int)strlen(menustring), &rect, DT_LEFT);
		SelectObject(m_hmemdc, hOldFont);

	}

	SetBkColor(m_hmemdc, oldbgcol);
	SetTextColor(m_hmemdc, oldtxtcol);
	SelectObject(m_hmemdc, oldbitmap);
	DeleteObject(backgroundBrush);
	SetRect(&rect, 0, 0, 640, 640);
	CopyToBuffer(rect, scrBuff, scrBuffSize);
}



#ifndef CAPTUREBLT
#define CAPTUREBLT  0x40000000
#endif

// Function to capture an area of the screen immediately prior to sending
// an update.
void
vncDesktop::CaptureScreen(const rfb::Rect &rect, BYTE *scrBuff, UINT scrBuffSize, BOOL capture)
{
	assert(rect.enclosed_by(m_bmrect));

	//Highcpu(100000000);
	//Beep(100, 100);
	if (capture)
	{
		// Select the memory bitmap into the memory DC
		HBITMAP oldbitmap;
		if ((oldbitmap = (HBITMAP)SelectObject(m_hmemdc, m_membitmap)) == NULL)
			return;

#if defined(_DEBUG)
		DWORD t = GetTimeFunction();
#endif
		// Capture screen into bitmap
		BOOL blitok = false;
		if (show_all_monitors)
		{
			blitok = BitBlt(m_hmemdc,
				rect.tl.x,
				rect.tl.y,
				(rect.br.x - rect.tl.x),
				(rect.br.y - rect.tl.y),
				m_hrootdc_Desktop,
				rect.tl.x + m_ScreenOffsetx,
				rect.tl.y + m_ScreenOffsety,
				((VNCOS.CaptureAlphaBlending() || m_server->AutoCapt() == 2) && !m_Black_window_active) ? (CAPTUREBLT | SRCCOPY) : SRCCOPY
			);
		}
		else
		{

			int xoffset = mymonitor[m_current_monitor].offsetx;
			int yoffset = mymonitor[m_current_monitor].offsety;

			blitok = BitBlt(m_hmemdc,
				rect.tl.x,
				rect.tl.y,
				(rect.br.x - rect.tl.x),
				(rect.br.y - rect.tl.y),
				m_hrootdc_Desktop, rect.tl.x + xoffset, rect.tl.y + yoffset, ((VNCOS.CaptureAlphaBlending() || m_server->AutoCapt() == 2) && !m_Black_window_active) ? (CAPTUREBLT | SRCCOPY) : SRCCOPY);
		}
		/*#if defined(_DEBUG)
			DWORD e = GetTimeFunction() - t;
			vnclog.Print(LL_INTWARN, VNCLOG("Blit (%u,%u - %u,%u) (%ux%u took %ums\n"),
				rect.tl.x, rect.tl.y, rect.br.x, rect.br.y,
				rect.width(), rect.height(), e);
		#endif*/
#if 0
		//*******************************
		static int cycle(0);
		static COLORREF c[] = { RGB(0xFF,0,0), RGB(0,0xFF,0),RGB(0,0,0xFF), RGB(0xFF, 0,0xFF), RGB(0,0xFF,0xFF) };
		HPEN pen = CreatePen(PS_SOLID | PS_INSIDEFRAME, 2, c[cycle]);
		HPEN oldpen = (HPEN)SelectObject(m_hmemdc, pen);
		SelectObject(m_hmemdc, GetStockObject(HOLLOW_BRUSH));
		Rectangle(m_hmemdc, rect.tl.x, rect.tl.y, rect.br.x, rect.br.y);
		SelectObject(m_hmemdc, oldpen);
		DeleteObject(pen);
		cycle = (cycle + 1) % (sizeof c / sizeof c[0]);
		//*******************************
#endif
	// Select the old bitmap back into the memory DC
		SelectObject(m_hmemdc, oldbitmap);

		if (blitok)
		{
			// Copy the new data to the screen buffer (CopyToBuffer optimises this if possible)
			CopyToBuffer(rect, scrBuff, scrBuffSize);
		}
	}
	else
	{
		CopyToBuffer(rect, scrBuff, scrBuffSize);
	}

}

// Add the mouse pointer to the buffer
void
vncDesktop::CaptureMouse(BYTE *scrBuff, UINT scrBuffSize)
{
	POINT CursorPos;
	ICONINFO IconInfo = { 0 };

	// If the mouse cursor handle is invalid then forget it
	if (m_hcursor == NULL)
		return;

	// Get the cursor position
	if (!GetCursorPos(&CursorPos))
		return;
	RECT testrect;
	testrect.top = m_Cliprect.tl.y + m_ScreenOffsety;
	testrect.bottom = m_Cliprect.br.y + m_ScreenOffsety;
	testrect.left = m_Cliprect.tl.x + m_ScreenOffsetx;
	testrect.right = m_Cliprect.br.x + m_ScreenOffsetx;
	if (!PtInRect(&testrect, CursorPos)) 
		return;

	// Translate position for hotspot
	if (GetIconInfo(m_hcursor, &IconInfo))
	{
		CursorPos.x -= ((int)IconInfo.xHotspot);
		CursorPos.y -= ((int)IconInfo.yHotspot);
		/// Buffer has (0,0) coordinates, Cursor (screencoordinates)
		CursorPos.x -= m_ScreenOffsetx;
		CursorPos.y -= m_ScreenOffsety;
		///

		if (CursorPos.x <= m_bmrect.tl.x || CursorPos.y <= m_bmrect.tl.y || CursorPos.x >= m_bmrect.br.x || CursorPos.y >= m_bmrect.br.y)
		{
			if ((m_cursorpos.tl.x <= m_bmrect.tl.x) ||
				(m_cursorpos.tl.y <= m_bmrect.tl.y) ||
				(m_cursorpos.br.x >= m_bmrect.br.x) ||
				(m_cursorpos.br.y >= m_bmrect.br.y))
			{
				m_cursorpos.tl.x = 0;
				m_cursorpos.tl.y = 0;
				m_cursorpos.br.x = 16;
				m_cursorpos.br.y = 16;
			}
			//Error, cursor isn't on the visbale display.
			if (IconInfo.hbmMask != NULL)
				DeleteObject(IconInfo.hbmMask);
			if (IconInfo.hbmColor != NULL)
				DeleteObject(IconInfo.hbmColor);
			return;
		}

		// Save the bounding rectangle
		m_cursorpos.tl = CursorPos;
		m_cursorpos.br = rfb::Point(GetSystemMetrics(SM_CXCURSOR),
			GetSystemMetrics(SM_CYCURSOR)).translate(CursorPos);
		// Clip the bounding rect to the screen
		// Copy the mouse cursor into the screen buffer, if any of it is visible
		m_cursorpos = m_cursorpos.intersect(m_bmrect);

		if (IconInfo.hbmMask && IconInfo.hbmColor)
		{
			HBITMAP oldbitmap;
			if ((oldbitmap = (HBITMAP)SelectObject(m_hmemdc, m_membitmap)) == NULL)
				return;
			HDC mdc1, mdc2;
			mdc1 = CreateCompatibleDC(m_hmemdc);
			mdc2 = CreateCompatibleDC(m_hmemdc);
			HBITMAP oldbmp1 = (HBITMAP)SelectObject(mdc1, IconInfo.hbmMask);
			HBITMAP oldbmp2 = (HBITMAP)SelectObject(mdc2, IconInfo.hbmColor);
			BitBlt(m_hmemdc, m_cursorpos.tl.x, m_cursorpos.tl.y, m_cursorpos.br.x - m_cursorpos.tl.x, m_cursorpos.br.y - m_cursorpos.tl.y, mdc1, 0, 0, SRCAND);
			BitBlt(m_hmemdc, m_cursorpos.tl.x, m_cursorpos.tl.y, m_cursorpos.br.x - m_cursorpos.tl.x, m_cursorpos.br.y - m_cursorpos.tl.y, mdc2, 0, 0, SRCINVERT);

			SelectObject(m_hmemdc, oldbitmap);
			SelectObject(mdc1, oldbmp1);
			SelectObject(mdc2, oldbmp2);
			DeleteDC(mdc1);
			DeleteDC(mdc2);
		}
		else
		{

			// Select the memory bitmap into the memory DC
			HBITMAP oldbitmap;
			if ((oldbitmap = (HBITMAP)SelectObject(m_hmemdc, m_membitmap)) == NULL)
				return;

			// Draw the cursor
			DrawIconEx(
				m_hmemdc,									// handle to device context 
				CursorPos.x, CursorPos.y,
				m_hcursor,									// handle to icon to draw 
				0, 0,										// width of the icon 
				0,											// index of frame in animated cursor 
				NULL,										// handle to background brush 
				DI_NORMAL | DI_COMPAT						// icon-drawing flags 
			);

			// Select the old bitmap back into the memory DC
			SelectObject(m_hmemdc, oldbitmap);
		}

		if (IconInfo.hbmMask != NULL)
			DeleteObject(IconInfo.hbmMask);
		if (IconInfo.hbmColor != NULL)
			DeleteObject(IconInfo.hbmColor);
	}


	if (!m_cursorpos.is_empty()) {
		CopyToBuffer(m_cursorpos, scrBuff, scrBuffSize);
	}
}


// CURSOR HANDLING
// Obtain cursor image data in server's local format.
// The length of databuf[] should be at least (width * height * 4).
BOOL
vncDesktop::GetRichCursorData(BYTE *databuf, HCURSOR hcursor, int width, int height)
{
	// Protect the memory bitmap (is it really necessary here?)
	omni_mutex_lock l(m_update_lock, 278);

	// Create bitmap, select it into memory DC
	HBITMAP membitmap = CreateCompatibleBitmap(m_hrootdc_Desktop, width, height);
	if (membitmap == NULL) {
		return FALSE;
	}
	HBITMAP oldbitmap = (HBITMAP)SelectObject(m_hmemdc, membitmap);
	if (oldbitmap == NULL) {
		DeleteObject(membitmap);
		return FALSE;
	}

	// Draw the cursor
	DrawIconEx(m_hmemdc, 0, 0, hcursor, 0, 0, 0, NULL, DI_IMAGE);
	SelectObject(m_hmemdc, oldbitmap);

	// Prepare BITMAPINFO structure (copy most m_bminfo fields)
	BITMAPINFO *bmi = (BITMAPINFO *)calloc(1, sizeof(BITMAPINFO) + 256 * sizeof(RGBQUAD));
	int lines = 0;
	if (bmi)
	{
		memcpy(bmi, &m_bminfo.bmi, sizeof(BITMAPINFO) + 256 * sizeof(RGBQUAD));
		bmi->bmiHeader.biWidth = width;
		bmi->bmiHeader.biHeight = -height;

		// Clear data buffer and extract RGB data
		memset(databuf, 0x00, width * height * 4);
		lines = GetDIBits(m_hmemdc, membitmap, 0, height, databuf, bmi, DIB_RGB_COLORS);

		// Cleanup
		free(bmi);
	}
	DeleteObject(membitmap);

	return (lines != 0);
}

// Return the current mouse pointer position
rfb::Rect
vncDesktop::MouseRect()
{
	return m_cursorpos;
}

void
vncDesktop::SetCursor(HCURSOR cursor)
{
	if (cursor == NULL)
		m_hcursor = m_hdefcursor;
	else
	{
		m_hOldcursor = m_hcursor; // sf@2002
		m_hcursor = cursor;
	}
}

// Manipulation of the clipboard
void vncDesktop::SetClipText(LPSTR rfbStr)
{
	// Open the system clipboard
	if (OpenClipboard(m_hwnd))
	{
		// Empty it
		if (EmptyClipboard())
		{
			// adzm - 2010-07 - Extended clipboard
			HANDLE hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, strlen(rfbStr) + 1);

			if (hMem != NULL)
			{
				LPSTR pMem = (char*)GlobalLock(hMem);

				// Get the data
				if (pMem)
				{
					strcpy_s(pMem, strlen(rfbStr) + 1, rfbStr);
					// Tell the clipboard
					GlobalUnlock(hMem);
					SetClipboardData(CF_TEXT, hMem);
				}
			}
		}
	}

	// Now close it
	CloseClipboard();
}

// adzm - 2010-07 - Extended clipboard
void vncDesktop::SetClipTextEx(ExtendedClipboardDataMessage& extendedClipboardDataMessage)
{
	bool bRestored = false;
	{
		ClipboardData newClipboard;

		if (newClipboard.Restore(Window(), extendedClipboardDataMessage)) {
			vnclog.Print(LL_INTINFO, VNCLOG("Set extended clipboard data\n"));

			bRestored = true;
			newClipboard.FreeData();
		}
		else {
			vnclog.Print(LL_INTWARN, VNCLOG("Failed to set extended clipboard data\n"));
		}
	}
}

// INTERNAL METHODS

inline void
vncDesktop::MaskToMaxAndShift(DWORD mask, CARD16 &max, CARD8 &shift)
{
	for (shift = 0; (mask & 1) == 0; shift++)
		mask >>= 1;
	max = (CARD16)mask;
}

// Copy data from the memory bitmap into a buffer
void
vncDesktop::CopyToBuffer(const rfb::Rect &rect, BYTE *destbuff, UINT destbuffsize)
{
	// Finish drawing anything in this thread 
	// Wish we could do this for the whole system - maybe we should
	// do something with LockWindowUpdate here.
	GdiFlush();

	// Are we being asked to blit from the DIBsection to itself?
	if (destbuff == m_DIBbits) {
		// Yes.  Ignore the request!
		return;
	}

	int y_inv;
	BYTE * destbuffpos;

	// Calculate the scanline-ordered y position to copy from
	y_inv = m_scrinfo.framebufferHeight - rect.tl.y - (rect.br.y - rect.tl.y);

	// Calculate where in the output buffer to put the data
	destbuffpos = destbuff + (m_bytesPerRow * rect.tl.y);

	// Set the number of bytes for GetDIBits to actually write
	// NOTE : GetDIBits pads the destination buffer if biSizeImage < no. of bytes required
	m_bminfo.bmi.bmiHeader.biSizeImage = (rect.br.y - rect.tl.y) * m_bytesPerRow;

	// Get the actual bits from the bitmap into the bit buffer
	// If fast (DIBsection) blits are disabled then use the old GetDIBits technique
	if (m_DIBbits == NULL) {
		if (GetDIBits(m_hmemdc, m_membitmap, y_inv,
			(rect.br.y - rect.tl.y), destbuffpos,
			&m_bminfo.bmi, DIB_RGB_COLORS) == 0)
		{
#ifdef _MSC_VER
			_RPT1(_CRT_WARN, "vncDesktop : [1] GetDIBits failed! %d\n", GetLastError());
			_RPT3(_CRT_WARN, "vncDesktop : thread = %d, DC = %d, bitmap = %d\n", omni_thread::self(), m_hmemdc, m_membitmap);
			_RPT2(_CRT_WARN, "vncDesktop : y = %d, height = %d\n", y_inv, (rect.br.y - rect.tl.y));
#endif
		}
	}
	else {
		// Fast blits are enabled.  [I have a sneaking suspicion this will never get used, unless
		// something weird goes wrong in the code.  It's here to keep the function general, though!]

		int bytesPerPixel = m_scrinfo.format.bitsPerPixel / 8;
		BYTE *srcbuffpos = (BYTE*)m_DIBbits;

		srcbuffpos += (m_bytesPerRow * rect.tl.y) + (bytesPerPixel * rect.tl.x);
		destbuffpos += bytesPerPixel * rect.tl.x;

		int widthBytes = (rect.br.x - rect.tl.x) * bytesPerPixel;

		for (int y = rect.tl.y; y < rect.br.y; y++)
		{
			memcpy(destbuffpos, srcbuffpos, widthBytes);
			srcbuffpos += m_bytesPerRow;
			destbuffpos += m_bytesPerRow;
		}
	}
}

// Routine to find out which windows have moved
// If copyrect detection isn't perfect then this call returns
// the copyrect destination region, to allow the caller to check
// for mistakes
bool
vncDesktop::CalcCopyRects(rfb::UpdateTracker &tracker)
{
	HWND foreground = GetForegroundWindow();
	RECT foreground_rect;

	// Actually, we just compare the new and old foreground window & its position
	if (foreground != m_foreground_window) {
		m_foreground_window = foreground;
		// Is the window invisible or can we not get its rect?
		if (!IsWindowVisible(foreground) ||
			!GetWindowRect(foreground, &foreground_rect)) {
			//Buffer coordinates
			m_foreground_window_rect.clear();
		}
		else {
			foreground_rect.left -= m_ScreenOffsetx;
			foreground_rect.right -= m_ScreenOffsetx;
			foreground_rect.top -= m_ScreenOffsety;
			foreground_rect.bottom -= m_ScreenOffsety;
			m_foreground_window_rect = foreground_rect;
		}
	}
	else {
		// Same window is in the foreground - let's see if it's moved
		RECT destrect;
		rfb::Rect dest;
		rfb::Point source;

		// Get the window rectangle
		if (IsWindowVisible(foreground) && GetWindowRect(foreground, &destrect))
		{
			//vnclog.Print(LL_INTERR, VNCLOG("foreground %i %i %i %i\n"),destrect.left,destrect.right,destrect.top,destrect.bottom);
			//screen to buffer coordinates
			destrect.left -= m_ScreenOffsetx;
			destrect.right -= m_ScreenOffsetx;
			destrect.top -= m_ScreenOffsety;
			destrect.bottom -= m_ScreenOffsety;
			//vnclog.Print(LL_INTERR, VNCLOG("foreground %i %i %i %i\n"),destrect.left,destrect.right,destrect.top,destrect.bottom);

			//We cant have negative coordinates
			//move window on desktop 1 and show desktop 2
			if (destrect.left < 0 || destrect.right < 0 || destrect.top < 0 || destrect.bottom < 0) return false;
			// we also can have to big coordinates
			// move window on desktop2 and show desktop 1
			if (destrect.left > m_Cliprect.br.x || destrect.right > m_Cliprect.br.x || destrect.top > m_Cliprect.br.y || destrect.bottom > m_Cliprect.br.y)
				return false;


			rfb::Rect old_foreground_window_rect = m_foreground_window_rect;
			source = m_foreground_window_rect.tl;
			m_foreground_window_rect = dest = destrect;
			//vnclog.Print(LL_INTERR, VNCLOG("dest %i %i %i %i\n"),dest.tl.x,dest.br.x,dest.tl.y,dest.br.y);
			//vnclog.Print(LL_INTERR, VNCLOG("source %i %i \n"),source.x,source.y);

			if (!dest.is_empty() && !old_foreground_window_rect.is_empty())
			{
				// Got the destination position.  Now send to clients!
				if (!source.equals(dest.tl))
				{
					rfb::Point delta;
					delta = rfb::Point(dest.tl.x - source.x, dest.tl.y - source.y);
					//	if (dest.tl.x-source.x==0 || dest.tl.y-source.y==0) return false;
						//vnclog.Print(LL_INTERR, VNCLOG("delta %i %i \n"),delta.x,delta.y);

						// Clip the destination rectangle
					dest = dest.intersect(m_bmrect);
					//vnclog.Print(LL_INTERR, VNCLOG("dest2 %i %i %i %i\n"),dest.tl.x,dest.br.x,dest.tl.y,dest.br.y);
					if (dest.is_empty()) return false;
					// Clip the source rectangle
					dest = dest.translate(delta.negate()).intersect(m_bmrect);
					//vnclog.Print(LL_INTERR, VNCLOG("dest3 %i %i %i %i\n"),dest.tl.x,dest.br.x,dest.tl.y,dest.br.y);
					if (dest.tl.x < 0 || dest.br.x < 0 || dest.tl.y < 0 || dest.br.y < 0) return false;
					if (dest.tl.x > m_Cliprect.br.x || dest.br.x > m_Cliprect.br.x || dest.tl.y > m_Cliprect.br.y || dest.br.y > m_Cliprect.br.y)
						return false;
					m_buffer.ClearCacheRect(dest);
					dest = dest.translate(delta);
					//vnclog.Print(LL_INTERR, VNCLOG("dest4 %i %i %i %i\n"),dest.tl.x,dest.br.x,dest.tl.y,dest.br.y);
					m_buffer.ClearCacheRect(dest);
					if (!dest.is_empty()) {
						tracker.add_copied(dest, delta);
						return true;
					}
				}
			}
		}
		else {
			m_foreground_window_rect.clear();
		}
	}
	return false;
}


// Modif rdv@2002 Dis/enable input
void
vncDesktop::SetDisableInput()
{
	CARD32 state;
	state = !block_input(false);
	m_server->NotifyClients_StateChange(rfbServerRemoteInputsState, state);
}


// SW
void vncDesktop::SetSW(int x, int y)
{
	POINT point;
	point.x = x;
	point.y = y;
	MONITORINFO monitorinfo;
	monitorinfo.cbSize = sizeof(MONITORINFO);

	helper::DynamicFn<LPGETMONITORINFO> GetMonitorInfo("USER32", "GetMonitorInfoA");
	helper::DynamicFn<LPMONITOTFROMPOINT> MonitorFromPoint("USER32", "MonitorFromPointA");

	if (x <= 5 && y <= 5 && x > -5 && y > -5)
	{
		if (m_screenCapture) {
			m_current_monitor++;
			if (m_current_monitor == nr_monitors) {
				m_current_monitor = MULTI_MON_ALL;
				m_buffer.SetAllMonitors(true);
			}
			else if (m_current_monitor == MULTI_MON_ALL + 1) {
				m_current_monitor = MULTI_MON_PRIMARY;
				m_buffer.SetAllMonitors(false);
			}
		}
		else {
			if (m_current_monitor == MULTI_MON_ALL) {
				m_current_monitor = MULTI_MON_PRIMARY;
				m_buffer.SetAllMonitors(false);
			}
			else {
				m_current_monitor = MULTI_MON_ALL;
				m_buffer.SetAllMonitors(true);
			}
		}
	}
	return;
}

/***************************************************************************
* UltraVNC use 2 different software hooking methods
* hookdll: Hook the messages between the windows (nt,9.x)
* ddihook: Hook the messages to the video buffer (9.x)
* Driver hooking
* Driver hooking is done at kernel level.
* and exist of 2 parts
* update mechanism: rectangles are send from kernel to vnc (shared mem)
* shared video buffer: direct access the memeory without bitblit
*  m_hookdriver: use driver update mechanism (ddihook in case of 9.x)
*  m_hookdll: use software update mechanism
***************************************************************************/

// Modif rdv@2002 - v1.1.x - videodriver

BOOL vncDesktop::VideoBuffer()
{
	if (m_screenCapture == NULL) 
		return FALSE;
	if (m_screenCapture->getChangeBuffer())
		return true;

	m_hookswitch = TRUE;
	Hookdll_Changed = TRUE;
	return FALSE;
}

DWORD WINAPI Warningbox_non_locked(LPVOID lpParam)
{
	MessageBoxSecure(NULL, "Current driver to old for this version \nUpdate driver or disable Video hook driver\n in the server properties window", "", 0);
	return 0;
}

// Modif rdv@2002 - v1.1.x - videodriver
BOOL vncDesktop::InitVideoDriver()
{
	omni_mutex_lock l(m_screenCapture_lock, 79);
	vnclog.Print(LL_INTERR, VNCLOG("Driver option is enabled\n"));
	// If m_screenCapture exist, the driver was activated.
	// This does not mean he is still active
	// Screen switching disable the driver at kernel level
	// The pointers to the shared memory still exist, but no more memeory
	// associated...This is the biggest risk when using the driver
	//

	// First check driver version
	if (m_screenCapture != NULL)
	{

		vnclog.Print(LL_INTERR, VNCLOG("Closing pending driver driver version\n"));
		if (m_screenCapture != NULL) delete m_screenCapture;

	}
	if (IsWindows8OrGreater())
	{
		int a = 0;
		m_screenCapture = new DeskDupEngine;
	}
	else
	{
		m_screenCapture = new VideoDriver;
	}

	//try to use the mirror driver if he is still active
	Checkmonitors();	
	if (nr_monitors == 1)
	{
		if (m_screenCapture != NULL) 
			m_screenCapture->videoDriver_start(mymonitor[MULTI_MON_PRIMARY].offsetx, mymonitor[MULTI_MON_PRIMARY].offsety, mymonitor[MULTI_MON_PRIMARY].Width, mymonitor[MULTI_MON_PRIMARY].Height, m_server->singleExtendRequested(), m_server->MaxFPS());
	}
	if (nr_monitors > 1)
	{
		if (m_screenCapture != NULL)
			m_screenCapture->videoDriver_start(mymonitor[MULTI_MON_ALL].offsetx, mymonitor[MULTI_MON_ALL].offsety, mymonitor[MULTI_MON_ALL].Width, mymonitor[MULTI_MON_ALL].Height, m_server->singleExtendRequested(), m_server->MaxFPS());
	}
	vnclog.Print(LL_INTERR, VNCLOG("Start Mirror driver\n"));

	// check if driver has mapped the shared memory
	if (m_screenCapture != NULL)
		if (!m_screenCapture->getChangeBuffer())
		{
			vnclog.Print(LL_INTERR, VNCLOG("Start Mirror driver/ddegine Failed\n"));
			vnclog.Print(LL_INTERR, VNCLOG("Using non driver mode\n"));
			if (m_screenCapture != NULL) delete m_screenCapture;
			m_screenCapture = NULL;
			// If driver selected and fialed to start default to hookdll
			// 
			m_hookdriver = false;
			m_hookdll = true;
			// sf@2002 - Necessary for the following InitHookSettings() call
			// Remember old states
			DriverWantedSet = true;
			DriverWanted = m_server->Driver();
			HookWanted = m_server->Hook();
			m_server->Driver(false);
			m_server->Hook(true);
			m_server->PollFullScreen(true);
			m_server->TurboMode(true);
			return false;
		}

	m_hookdriver = true;
	m_hookdll = false;

	if (m_screenCapture != NULL)
		if (m_screenCapture->getChangeBuffer())
		{
			vnclog.Print(LL_INTERR, VNCLOG("Driver Used\n"));
			vnclog.Print(LL_INTERR, VNCLOG("Shared memory mapped\n"));
			InvalidateRect(NULL, NULL, TRUE);
			if (m_screenCapture->getHScreenEvent() != NULL)
				trigger_events[6] = m_screenCapture->getHScreenEvent();
			if (m_screenCapture->getHPointerEvent() != NULL)
				trigger_events[7] = m_screenCapture->getHPointerEvent();	
			return true;
		}
	return true;
}


// Modif rdv@2002 - v1.1.x - videodriver
void vncDesktop::ShutdownVideoDriver()
{
	if (m_screenCapture == NULL)
		return;
	if (m_screenCapture != NULL)
	{
		delete m_screenCapture;
		m_screenCapture = NULL;
	}
}


//
// This proc sets the Desktop update handling params depending
// on tranitted settings and internal constraints.
// It is called from several places (InitHookSettings(), SetHookings()
// and from Desktop thread loop).
// 
void vncDesktop::SethookMechanism(BOOL hookall, BOOL hookdriver)
{
	m_hookswitch = false;
	// Force at least one updates handling method !
	if (!hookall && !hookdriver && !m_server->PollFullScreen())
	{
		hookall = TRUE;
		m_server->PollFullScreen(TRUE);
	}

	// sf@2002 - We forbid hoodll and hookdriver at the same time (pointless and high CPU load)
	if (!hookall && !hookdriver) { m_hookdll = false; m_hookdriver = false; }
	if (hookall && hookdriver) { m_hookdll = false; m_hookdriver = true; }
	if (hookall && !hookdriver) { m_hookdll = true; m_hookdriver = false; }
	if (!hookall && hookdriver) { m_hookdll = false; m_hookdriver = true; }

	BOOL old_On_Off_hookdll = On_Off_hookdll;
	if (m_hookdll) On_Off_hookdll = true;
	else On_Off_hookdll = false;
	if (old_On_Off_hookdll != On_Off_hookdll) Hookdll_Changed = true;
	else Hookdll_Changed = false;
	if (VNCOS.OS_VISTA || VNCOS.OS_WIN7 || VNCOS.OS_WIN8 || VNCOS.OS_WIN10) Hookdll_Changed = true;

	vnclog.Print(LL_INTERR, VNCLOG("Sethook_restart_wanted hook=%d driver=%d \r\n"), m_hookdll, m_hookdriver);
	if (Hookdll_Changed)
		vnclog.Print(LL_INTERR, VNCLOG("Hookdll status changed \r\n"));
	if ((m_hookdriver && !VideoBuffer()) || (!m_hookdriver && VideoBuffer()))
	{
		m_hookswitch = true;
		vnclog.Print(LL_INTERR, VNCLOG("Driver Status changed\r\n"));
	}

}
void vncDesktop::StartStopddihook(BOOL enabled)
{
	if (enabled)
	{
		STARTUPINFO ssi;
		PROCESS_INFORMATION ppi;
		m_hddihook = NULL;
		char szCurrentDir[MAX_PATH];
		if (GetModuleFileName(NULL, szCurrentDir, MAX_PATH))
		{
			char* p = strrchr(szCurrentDir, '\\');
			if (p == NULL) return;
			*p = '\0';
			strcat_s(szCurrentDir, "\\16bithlp.exe");
		}
		// Add ddi hook
		ZeroMemory(&ssi, sizeof(ssi));
		ssi.cb = sizeof(ssi);
		// Start the child process. 
		if (!CreateProcess(NULL, szCurrentDir, NULL, NULL, FALSE, 0, NULL, NULL, &ssi, &ppi))
		{
			vnclog.Print(LL_INTERR, VNCLOG("set ddihooks Failed\n"));
			ddihook = false;
		}
		else
		{
			vnclog.Print(LL_INTERR, VNCLOG("set ddihooks OK\n"));
			ddihook = true;
			WaitForInputIdle(ppi.hProcess, 10000);
			m_hddihook = ppi.hProcess;
		}
	}
	else
	{
		if (m_hddihook != NULL)
		{
			TerminateProcess(m_hddihook, 0);
			m_hddihook = NULL;
			ddihook = false;
		}
	}

}

void vncDesktop::StartStophookdll(BOOL enabled)
{
	PostMessage(m_hwnd, WM_HOOKCHANGE, enabled, 0);
}


//
//
//
void vncDesktop::InitHookSettings()
{
	SethookMechanism(m_server->Hook(), m_server->Driver());
}

void vncDesktop::PreventScreensaver(bool state)
{
	if (state && m_server->GetNoScreensaver()) {
		SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED | ES_AWAYMODE_REQUIRED);
	}
	else
		SetThreadExecutionState(ES_CONTINUOUS);
}

void vncDesktop::SetBlockInputState(bool newstate)
{
	CARD32 state;
	if (m_server->BlankMonitorEnabled())
	{
		if (!m_server->BlankInputsOnly())
		{
			if ((blankmonitorstate == newstate) && (newstate == 1))
			{
				SetBlankMonitor(0);
				blankmonitorstate = 0;
			}
			else
			{
				SetBlankMonitor(newstate);
				blankmonitorstate = newstate;
			}
		}
		m_bIsInputDisabledByClient = newstate;
		state = !block_input(false);

	}
	else state = !newstate;

	m_bIsInputDisabledByClient = !state;

	m_server->NotifyClients_StateChange(rfbServerRemoteInputsState, state);
}

bool vncDesktop::block_input(bool first)
{
	if (first)
	{
		old_Blockinput1 = m_bIsInputDisabledByClient ? 1 : 0;
		old_Blockinput2 = m_server->LocalInputsDisabled() ? 1 : 0;
		return false;
	}
	else
	{
		bool Blockinput_val;
		BOOL returnvalue;
		if (m_bIsInputDisabledByClient || m_server->LocalInputsDisabled())
		{
			Blockinput_val = true;
		}
		else
		{
			Blockinput_val = false;
		}

		/*#ifdef _DEBUG
						char			szText[256];
						sprintf_s(szText," blockinput %i %i %i %i\n",m_bIsInputDisabledByClient,m_server->LocalInputsDisabled(),old_Blockinput1,old_Blockinput2);
						SetLastError(0);
						OutputDebugString(szText);
		#endif*/

		if (old_Blockinput1 != (m_bIsInputDisabledByClient ? 1 : 0) || old_Blockinput2 != (m_server->LocalInputsDisabled() ? 1 : 0))
		{
			CARD32 state;
			state = !Blockinput_val;
			m_server->NotifyClients_StateChange(rfbServerRemoteInputsState, state);
		}
		if (pbi)
		{
			returnvalue = (*pbi)(Blockinput_val);
			DWORD aa = GetLastError();
			if (old_Blockinput != Blockinput_val && aa == 5)
			{
				if (m_hookinited)
					PostMessage(m_hwnd, WM_HOOKCHANGE, 2, 0);
				else Blockinput_val = false;
			}

		}
		old_Blockinput1 = m_bIsInputDisabledByClient ? 1 : 0;
		old_Blockinput2 = m_server->LocalInputsDisabled() ? 1 : 0;
		old_Blockinput = Blockinput_val;
		return Blockinput_val;
	}
}
